`include "mycpu.h"

module IF_stage(
    input                          clk            ,
    // (*mark_debug = "true"*)  input                          reset,
    input                          reset,
    //allwoin
    input                           ds_allowin,
    // input                           cancel_s,
    //brbus
    input  [`BR_BUS_WD       -1:0] br_bus         ,
    input   [63:0]                  csr_flush_pc,
    input                           csr_flush_o,
    input   [`DS_TO_BPU_BUS_WD -1:0] ds_to_bpu_bus, 
    //flush
    input  [`ES_FLUSH_BUS_WD -1:0] es_flush_bus,
    
    //to ds
    output                         fs_to_ds_valid ,
    output [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus   ,
    output [`BPU_TO_DS_BUS_WD -1:0] bpu_to_ds_bus ,
    // inst sram interface
    output        inst_sram_req   ,
    output        inst_sram_wr,
    output [ 1:0] inst_sram_size    ,
    output [ 3:0] inst_sram_wstrb  ,
    output [63:0] inst_sram_addr ,
    output [31:0] inst_sram_wdata,
    input         inst_sram_addr_ok,
    input         inst_sram_data_ok,
    input  [31:0] inst_sram_rdata
);
//修改内容
//一次读取的内容为64位，但是指令码只是32位
//PC与地址变成了64位
//RV取消了转移延迟槽，需要使用转移预测或者是隔拍停止的策略
//RV取消了转移延迟槽，对应的取指逻辑需要修改一下。
reg         cr_flush_o_reg;
reg         fs_valid;
reg         addr_alok;
reg         data_alok;
reg         data_alok2;
reg [31:0]  rdata_buf2;  
reg         cancel_first_back;
reg         br_bus_buf_s;
reg  [1:0]  count;
// (*mark_debug = "true"*) reg  [31:0] fs_pc;
reg  [63:0] fs_pc;
reg  [63:0] cr_to_fs_pc_reg;
reg  [63:0] br_target_buf;
reg  [31:0] rdata_buf;
reg         req;
wire        req_can;
wire        req_pre_is_target;
wire        br_bus_dir_valid;
wire        pre_is_target;
wire        br_ds_will_to_es;
wire        br_stall_dir;
wire        br_taken_dir;
wire        pre_ready_go;
wire        br_taken;
wire        fs_ready_go;
wire        fs_allowin;
wire        to_fs_valid;
wire        fs_ex;
wire        cr_flush_o;
wire        count_add;
wire        count_sub;
wire [63:0] seq_pc;
// (*mark_debug = "true"*) wire [31:0] nextpc;
wire [63:0] nextpc;
wire [63:0] cr_to_fs_pc;
wire [63:0] br_target;
wire [63:0] br_target_dir;
wire [31:0] fs_inst;
wire [31:0] rdata;
wire        es_flush;
wire [63:0] es_flush_pc;
////LiteBPU////
wire [63:0] x1_value;
wire [63:0] xn_value;
wire [63:0] bpu_pc;
wire [4:0]  rxn;
wire        bpu_taken;
wire        bpu_may_wait;
wire        ds_may_wait;
wire        req_wait;
// assign rdata = {inst_sram_rdata[7:0],inst_sram_rdata[15:8],inst_sram_rdata[23:16],inst_sram_rdata[31:24]};
assign rdata = inst_sram_rdata;

assign {
            br_stall_dir,
            br_taken_dir,
            br_target_dir
                } = br_bus;
// assign {    
//             cr_flush_o,
//             cr_to_fs_pc
//                 } = cr_to_fs_bus;
assign cr_flush_o = es_flush || csr_flush_o;
assign cr_to_fs_pc = csr_flush_o ? csr_flush_pc : es_flush_pc;
assign {es_flush,es_flush_pc} =es_flush_bus;
assign fs_to_ds_bus = {fs_ex,
                       fs_inst ,
                       bpu_pc  ,
                       fs_pc   };//1+32+64+64=161
assign    br_bus_dir_valid = ~br_stall_dir && br_taken_dir;
assign    pre_is_target    = ( br_bus_dir_valid || br_bus_buf_s) ;
assign    br_target        = br_bus_buf_s  ? br_target_buf    : br_target_dir;
                             
// pre-IF stage
assign pre_ready_go     = addr_alok && ~req_wait || inst_sram_req && inst_sram_addr_ok;
assign to_fs_valid      = pre_ready_go ;//�?
assign seq_pc           = fs_pc + 3'h4;
assign fs_ex            = fs_pc[1:0] != 2'h0 ;
assign br_ds_will_to_es = br_taken_dir && ds_allowin;
assign nextpc           =  cr_flush_o_reg    ? cr_to_fs_pc_reg : bpu_pc;
                        // (br_bus_buf_s || br_bus_dir_valid) && ~fs_valid ? seq_pc :
                        // bpu_taken              ? bpu_pc : 
                        //                                                   seq_pc;
assign count_add = inst_sram_req && inst_sram_addr_ok && !inst_sram_data_ok;
assign count_sub = inst_sram_data_ok && !( inst_sram_req && inst_sram_addr_ok);
// assign count_add = cancel_s && !inst_sram_data_ok;
// assign count_sub = inst_sram_data_ok && !cancel_s;
always @(posedge clk ) begin
    if (reset) begin
        cr_to_fs_pc_reg <= 32'b0;
    end
    else if (cr_flush_o) begin
        cr_to_fs_pc_reg <= cr_to_fs_pc;
    end

    if (reset || cr_flush_o || pre_is_target && pre_ready_go && fs_allowin && ds_allowin )    begin
        br_bus_buf_s <= 1'b0;//cr_flush_o 清空流水线的信号
    end
    else if ( br_ds_will_to_es  )  begin//转移指令即将离开ID进入EXE
        br_bus_buf_s     <= 1'b1;
        br_target_buf       <= br_target_dir;
    end


    if (cr_flush_o) begin
        cr_flush_o_reg <= 1'b1;
    end
    else if(reset || pre_ready_go && fs_allowin ) begin
        cr_flush_o_reg <= 1'b0;
    end

    if (fs_allowin && pre_ready_go || reset || cr_flush_o) begin
        addr_alok <= 1'b0;
    end
    else if (inst_sram_addr_ok && inst_sram_req ) begin
        addr_alok <= 1'b1;
    end

    if (reset || count == 2'b0 && !count_add || count == 2'h1 && count_sub ) begin
        cancel_first_back <= 1'b0;
    end
    else if ( cr_flush_o && !(  count == 2'b0 && !count_add || count == 2'h1 && count_sub)) begin
        cancel_first_back <= 1'b1;
    end

    if (reset) begin
        count <= 2'b0;
    end
    else if (count_add) begin
        count <= count + 1'b1;
    end
    else if (count_sub)begin
        count <= count -1'b1;
    end

end

// IF stage
assign fs_ready_go    = (data_alok || inst_sram_data_ok|| data_alok2 ) && ~cancel_first_back;
                //  &&  !(( br_bus_dir_valid || br_bus_buf_s) && ~pre_ready_go);
assign fs_allowin     = !fs_valid || fs_ready_go && ds_allowin;
assign fs_to_ds_valid =  fs_valid && fs_ready_go; //要进fs与fs准备，就可以出发�?
always @(posedge clk) begin
    if (reset || cr_flush_o ) begin
        fs_valid <= 1'b0;
    end
    else if (fs_allowin ) begin
        fs_valid <= to_fs_valid;
    end
    
    if (reset) begin
        // fs_pc <= 32'hbfbffffc;  //trick: to make nextpc be 0xbfc00000 during reset 
        // fs_pc <= 32'h7FFF_FFFc;
        fs_pc   <= 64'h00000000_7FFF_FFFc;
    end
    else if (to_fs_valid && fs_allowin ) begin
        fs_pc <= nextpc;
    end
    
    if ( fs_ready_go && ds_allowin || cr_flush_o || reset) begin
        data_alok <= 1'b0;
    end

    else  if (inst_sram_data_ok && !fs_allowin &&~cancel_first_back && !data_alok2) begin
        data_alok <= 1'b1;
        // rdata_buf <= inst_sram_rdata;
    end
    if (reset || data_alok2 && fs_ready_go  && ds_allowin && !data_alok||  cr_flush_o) begin
        data_alok2 <= 1'b0;
    end
    else if (data_alok  && inst_sram_data_ok) begin
        data_alok2 <= 1'b1;
        rdata_buf2 <= rdata;

    end
    if (reset || pre_ready_go && !fs_allowin  || cr_flush_o ) begin
        req <= 1'b0;
    end
    else if (req_can) begin
        req <= 1'b1;
    end

    
    if (inst_sram_data_ok  && !fs_allowin && !data_alok ) begin
        rdata_buf <= rdata;
        
    end

end

// assign req_pre_is_target = !data_alok && data_alok2 && count == 2'b0 && pre_is_target;

assign req_can =  (!pre_ready_go && !fs_allowin || !pre_ready_go && fs_allowin || pre_ready_go && fs_allowin)
		&& !inst_sram_data_ok
                && (!data_alok2 )  
                && (!data_alok || data_alok && count == 2'b0) 
                && !cancel_first_back
                && fs_allowin 
                && !fs_valid;
////LiteBPU////
// wire [63:0] x1_value;
// wire [63:0] xn_value;
// wire [4:0]  rxn;
// wire        bpu_taken;
// wire        bpu_may_wait;
// wire        ds_may_wait;
// wire        req_wait;
assign req_wait      = ds_may_wait && bpu_may_wait;
assign bpu_to_ds_bus = rxn;
assign {ds_may_wait,xn_value,x1_value} = ds_to_bpu_bus;

LiteBPU u_bpu(
    .clk        (clk),
    .reset      (reset),
    .inst       (inst_sram_rdata),
    .x1_value   (x1_value),
    .xn_value   (xn_value),
    .pc         (fs_pc),
    .rxn        (rxn),
    .bpu_pc     (bpu_pc),
    .bpu_taken  (bpu_taken),
    .bpu_may_wait (bpu_may_wait)
);


assign inst_sram_req = req && !req_wait && fs_allowin;
assign inst_sram_wr     = 1'b0;
assign inst_sram_size   = 2'h2;
assign inst_sram_wstrb  = 4'h0;
assign inst_sram_addr   = inst_sram_req ?  nextpc : 64'b0;
assign inst_sram_wdata  = 32'b0;
assign fs_inst =data_alok ? rdata_buf: 
                data_alok2? rdata_buf2 : rdata;

endmodule
